  /*
  Copyright 2017 zhpanvip The BannerViewPager Open Source Project

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
   */
  package com.demo.bannerview;

  import android.content.Context;
  import android.graphics.Canvas;
  import android.graphics.Path;
  import android.graphics.RectF;
  import android.os.Build;
  import android.os.Bundle;
  import android.os.Handler;
  import android.os.Looper;
  import android.os.Parcelable;
  import android.util.AttributeSet;
  import android.view.MotionEvent;
  import android.view.View;
  import android.widget.RelativeLayout;

  import androidx.annotation.ColorInt;
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.lifecycle.Lifecycle;
  import androidx.lifecycle.LifecycleObserver;
  import androidx.lifecycle.OnLifecycleEvent;
  import androidx.recyclerview.widget.RecyclerView;
  import androidx.viewpager2.widget.ViewPager2;

  import com.demo.bannerview.annotation.AIndicatorGravity;
  import com.demo.bannerview.annotation.APageStyle;
  import com.demo.bannerview.annotation.Visibility;
  import com.demo.bannerview.constants.IndicatorGravity;
  import com.demo.bannerview.constants.PageStyle;
  import com.demo.bannerview.indicator.IndicatorView;
  import com.demo.bannerview.indicator.annotation.AIndicatorSlideMode;
  import com.demo.bannerview.indicator.annotation.AIndicatorStyle;
  import com.demo.bannerview.indicator.base.IIndicator;
  import com.demo.bannerview.indicator.option.IndicatorOptions;
  import com.demo.bannerview.manager.BannerManager;
  import com.demo.bannerview.manager.BannerOptions;
  import com.demo.bannerview.provider.ReflectLayoutManager;
  import com.demo.bannerview.provider.ViewStyleSetter;
  import com.demo.bannerview.transform.ScaleInTransformer;
  import com.demo.bannerview.utils.BannerUtils;


  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;

  import com.module.commonui.R;

  @SuppressWarnings({ "unused", "UnusedReturnValue" })
  public class BannerViewPager<T> extends RelativeLayout implements LifecycleObserver {

    private int currentPosition;

    private boolean isCustomIndicator;

    private boolean isLooping;

    private OnPageClickListener mOnPageClickListener;

    private IIndicator mIndicatorView;

    private RelativeLayout mIndicatorLayout;

    private ViewPager2 mViewPager;

    private BannerManager mBannerManager;

    private final Handler mHandler = new Handler(Looper.getMainLooper());

    private BaseBannerAdapter<T> mBannerPagerAdapter;

    private ViewPager2.OnPageChangeCallback onPageChangeCallback;

    private final Runnable mRunnable = this::handlePosition;

    private RectF mRadiusRectF;
    private Path mRadiusPath;

    private int startX, startY;

    private final ViewPager2.OnPageChangeCallback mOnPageChangeCallback =
        new ViewPager2.OnPageChangeCallback() {
          @Override
          public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            super.onPageScrolled(position, positionOffset, positionOffsetPixels);
            pageScrolled(position, positionOffset, positionOffsetPixels);
          }

          @Override
          public void onPageSelected(int position) {
            super.onPageSelected(position);
            pageSelected(position);
          }

          @Override
          public void onPageScrollStateChanged(int state) {
            super.onPageScrollStateChanged(state);
            pageScrollStateChanged(state);
          }
        };

    public BannerViewPager(Context context) {
      this(context, null);
    }

    public BannerViewPager(Context context, AttributeSet attrs) {
      this(context, attrs, 0);
    }

    public BannerViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
      super(context, attrs, defStyleAttr);
      init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
      mBannerManager = new BannerManager();
      mBannerManager.initAttrs(context, attrs);
      initView();
    }

    private void initView() {
      inflate(getContext(), R.layout.bvp_layout, this);
      mViewPager = findViewById(R.id.vp_main);
      mIndicatorLayout = findViewById(R.id.bvp_layout_indicator);
      mViewPager.setPageTransformer(mBannerManager.getCompositePageTransformer());
    }

    @Override
    protected void onDetachedFromWindow() {
      if (mBannerManager != null && isStopLoopWhenDetachedFromWindow()) {
        stopLoop();
      }
      super.onDetachedFromWindow();
    }

    @Override
    protected void onAttachedToWindow() {
      super.onAttachedToWindow();
      if (mBannerManager != null && isStopLoopWhenDetachedFromWindow()) {
        startLoop();
      }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
      switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
          isLooping = true;
          stopLoop();
          break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_OUTSIDE:
          isLooping = false;
          startLoop();
          break;
        default:
          break;
      }
      return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
      boolean doNotNeedIntercept = !mViewPager.isUserInputEnabled()
          || mBannerPagerAdapter != null
          && mBannerPagerAdapter.getData().size() <= 1;
      if (doNotNeedIntercept) {
        return super.onInterceptTouchEvent(ev);
      }
      switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
          startX = (int) ev.getX();
          startY = (int) ev.getY();
          getParent().requestDisallowInterceptTouchEvent(!mBannerManager
              .getBannerOptions().isDisallowParentInterceptDownEvent());
          break;
        case MotionEvent.ACTION_MOVE:
          int endX = (int) ev.getX();
          int endY = (int) ev.getY();
          int disX = Math.abs(endX - startX);
          int disY = Math.abs(endY - startY);
          int orientation = mBannerManager.getBannerOptions().getOrientation();
          if (orientation == ViewPager2.ORIENTATION_VERTICAL) {
            onVerticalActionMove(endY, disX, disY);
          } else if (orientation == ViewPager2.ORIENTATION_HORIZONTAL) {
            onHorizontalActionMove(endX, disX, disY);
          }
          break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
          getParent().requestDisallowInterceptTouchEvent(false);
          break;
        case MotionEvent.ACTION_OUTSIDE:
        default:
          break;
      }
      return super.onInterceptTouchEvent(ev);
    }

    private void onVerticalActionMove(int endY, int disX, int disY) {
      if (disY > disX) {
        boolean canLoop = mBannerManager.getBannerOptions().isCanLoop();
        if (!canLoop) {
          if (currentPosition == 0 && endY - startY > 0) {
            getParent().requestDisallowInterceptTouchEvent(false);
          } else {
            getParent().requestDisallowInterceptTouchEvent(currentPosition != getData().size() - 1
                || endY - startY >= 0);
          }
        } else {
          getParent().requestDisallowInterceptTouchEvent(true);
        }
      } else if (disX > disY) {
        getParent().requestDisallowInterceptTouchEvent(false);
      }
    }

    private void onHorizontalActionMove(int endX, int disX, int disY) {
      if (disX > disY) {
        boolean canLoop = mBannerManager.getBannerOptions().isCanLoop();
        if (!canLoop) {
          if (currentPosition == 0 && endX - startX > 0) {
            getParent().requestDisallowInterceptTouchEvent(false);
          } else {
            getParent().requestDisallowInterceptTouchEvent(currentPosition != getData().size() - 1
                || endX - startX >= 0);
          }
        } else {
          getParent().requestDisallowInterceptTouchEvent(true);
        }
      } else if (disY > disX) {
        getParent().requestDisallowInterceptTouchEvent(false);
      }
    }

    public void pageScrollStateChanged(int state) {
      if (mIndicatorView != null) {
        mIndicatorView.onPageScrollStateChanged(state);
      }
      if (onPageChangeCallback != null) {
        onPageChangeCallback.onPageScrollStateChanged(state);
      }
    }

    public void pageSelected(int position) {
      int size = mBannerPagerAdapter.getListSize();
      boolean canLoop = mBannerManager.getBannerOptions().isCanLoop();
      currentPosition = BannerUtils.getRealPosition(position, size);
      boolean needResetCurrentItem =
          size > 0 && canLoop && (position == 0 || position == BaseBannerAdapter.MAX_VALUE - 1);
      if (needResetCurrentItem) {
        resetCurrentItem(currentPosition);
      }
      if (onPageChangeCallback != null) {
        onPageChangeCallback.onPageSelected(currentPosition);
      }
      if (mIndicatorView != null) {
        mIndicatorView.onPageSelected(currentPosition);
      }
    }

    public void pageScrolled(int position, float positionOffset, int positionOffsetPixels) {
      int listSize = mBannerPagerAdapter.getListSize();
      boolean canLoop = mBannerManager.getBannerOptions().isCanLoop();
      int realPosition = BannerUtils.getRealPosition(position, listSize);
      if (listSize > 0) {
        if (onPageChangeCallback != null) {
          onPageChangeCallback.onPageScrolled(realPosition, positionOffset, positionOffsetPixels);
        }
        if (mIndicatorView != null) {
          mIndicatorView.onPageScrolled(realPosition, positionOffset, positionOffsetPixels);
        }
      }
    }

    private void handlePosition() {
      if (mBannerPagerAdapter != null && mBannerPagerAdapter.getListSize() > 1 && isAutoPlay() && getInterval() > 0) {
        mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1, mBannerManager.getBannerOptions().isAutoScrollSmoothly());
        mHandler.postDelayed(mRunnable, getInterval());
      }
    }

    public void initBannerData(boolean registerOnPageChangeCallback) {
      List<T> list = mBannerPagerAdapter.getData();
      if (list != null) {
        setIndicatorValues(list);
        setupViewPager(list, registerOnPageChangeCallback);
        initRoundCorner();
      }
    }

    private void setIndicatorValues(List<? extends T> list) {
      BannerOptions bannerOptions = mBannerManager.getBannerOptions();
      mIndicatorLayout.setVisibility(bannerOptions.getIndicatorVisibility());
      bannerOptions.resetIndicatorOptions();
      if (isCustomIndicator) {
        mIndicatorLayout.removeAllViews();
      } else if (mIndicatorView == null) {
        mIndicatorView = new IndicatorView(getContext());
      }
      initIndicator(bannerOptions.getIndicatorOptions(), list);
    }

    private void initIndicator(IndicatorOptions indicatorOptions, List<? extends T> list) {
      if (((View) mIndicatorView).getParent() == null) {
        mIndicatorLayout.removeAllViews();
        mIndicatorLayout.addView((View) mIndicatorView);
        initIndicatorSliderMargin();
        initIndicatorGravity();
      }
      mIndicatorView.setIndicatorOptions(indicatorOptions);
      indicatorOptions.setPageSize(list.size());
      mIndicatorView.notifyDataChanged();
    }

    private void initIndicatorGravity() {
      LayoutParams layoutParams =
          (LayoutParams) ((View) mIndicatorView).getLayoutParams();
      switch (mBannerManager.getBannerOptions().getIndicatorGravity()) {
        case IndicatorGravity.CENTER:
          layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
          break;
        case IndicatorGravity.START:
          layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
          break;
        case IndicatorGravity.END:
          layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
          break;
        default:
          break;
      }
    }

    private void initIndicatorSliderMargin() {
      MarginLayoutParams layoutParams =
          (MarginLayoutParams) ((View) mIndicatorView).getLayoutParams();
      BannerOptions.IndicatorMargin indicatorMargin =
          mBannerManager.getBannerOptions().getIndicatorMargin();
      if (indicatorMargin == null) {
        int dp10 = BannerUtils.dp2px(10);
        layoutParams.setMargins(dp10, dp10, dp10, dp10);
      } else {
        layoutParams.setMargins(indicatorMargin.getLeft(), indicatorMargin.getTop(),
            indicatorMargin.getRight(), indicatorMargin.getBottom());
      }
    }

    private boolean isStopLoopWhenDetachedFromWindow() {
      return mBannerManager.getBannerOptions().isStopLoopWhenDetachedFromWindow();
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
      float[] roundRectRadiusArray = mBannerManager.getBannerOptions().getRoundRectRadiusArray();
      if (mRadiusRectF != null && mRadiusPath != null && roundRectRadiusArray != null) {
        mRadiusRectF.right = this.getWidth();
        mRadiusRectF.bottom = this.getHeight();
        mRadiusPath.addRoundRect(mRadiusRectF, roundRectRadiusArray, Path.Direction.CW);
        canvas.clipPath(mRadiusPath);
      }
      super.dispatchDraw(canvas);
    }

    private void initRoundCorner() {
      int roundCorner = mBannerManager.getBannerOptions().getRoundRectRadius();
      if (roundCorner > 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        ViewStyleSetter.applyRoundCorner(this, roundCorner);
      }
    }

    private void setupViewPager(List<T> list, boolean registerOnPageChangeCallback) {
      if (mBannerPagerAdapter == null) {
        throw new NullPointerException("You must set adapter for BannerViewPager");
      }
      BannerOptions bannerOptions = mBannerManager.getBannerOptions();
      if (bannerOptions.getScrollDuration() != 0) {
        ReflectLayoutManager.reflectLayoutManager(mViewPager, bannerOptions.getScrollDuration());
      }
      currentPosition = 0;
      mBannerPagerAdapter.setCanLoop(bannerOptions.isCanLoop());
      mBannerPagerAdapter.setPageClickListener(mOnPageClickListener);

      mViewPager.setAdapter(mBannerPagerAdapter);
      if (isCanLoopSafely()) {
        mViewPager.setCurrentItem(BannerUtils.getOriginalPosition(list.size()), false);
      }
      mViewPager.unregisterOnPageChangeCallback(mOnPageChangeCallback);
      if (registerOnPageChangeCallback){
        mViewPager.registerOnPageChangeCallback(mOnPageChangeCallback);
      }

      mViewPager.setOrientation(bannerOptions.getOrientation());
      mViewPager.setOffscreenPageLimit(bannerOptions.getOffScreenPageLimit());
      initRevealWidth(bannerOptions);
      initPageStyle(bannerOptions.getPageStyle());
      startLoop();
    }


    public void setAdapter(RecyclerView.Adapter<RecyclerView.ViewHolder> adapter){

      BannerOptions bannerOptions = mBannerManager.getBannerOptions();
      mViewPager.setAdapter(adapter);
      if (isCanLoopSafely()) {
        mViewPager.setCurrentItem(BannerUtils.getOriginalPosition(0), false);
      }
      mViewPager.unregisterOnPageChangeCallback(mOnPageChangeCallback);
      mViewPager.registerOnPageChangeCallback(mOnPageChangeCallback);
      mViewPager.setOrientation(bannerOptions.getOrientation());
      mViewPager.setOffscreenPageLimit(bannerOptions.getOffScreenPageLimit());
      initRevealWidth(bannerOptions);
      initPageStyle(bannerOptions.getPageStyle());
      startLoop();
    }

    private void initRevealWidth(BannerOptions bannerOptions) {
      int rightRevealWidth = bannerOptions.getRightRevealWidth();
      int leftRevealWidth = bannerOptions.getLeftRevealWidth();
      if (leftRevealWidth != BannerOptions.DEFAULT_REVEAL_WIDTH || rightRevealWidth != BannerOptions.DEFAULT_REVEAL_WIDTH) {
        RecyclerView recyclerView = (RecyclerView) mViewPager.getChildAt(0);
        int orientation = bannerOptions.getOrientation();
        int padding2 = bannerOptions.getPageMargin() + rightRevealWidth;
        int padding1 = bannerOptions.getPageMargin() + leftRevealWidth;
        if (orientation == ViewPager2.ORIENTATION_HORIZONTAL) {
          recyclerView.setPadding(padding1, 0, padding2, 0);
        } else if (orientation == ViewPager2.ORIENTATION_VERTICAL) {
          recyclerView.setPadding(0, padding1, 0, padding2);
        }
        recyclerView.setClipToPadding(false);
      }
      mBannerManager.createMarginTransformer();
    }

    private void initPageStyle(@APageStyle int pageStyle) {
      float pageScale = mBannerManager.getBannerOptions().getPageScale();
      if (pageStyle == PageStyle.MULTI_PAGE_OVERLAP) {
        mBannerManager.setMultiPageStyle(true, pageScale);
      } else if (pageStyle == PageStyle.MULTI_PAGE_SCALE) {
        mBannerManager.setMultiPageStyle(false, pageScale);
      }
    }

    private void resetCurrentItem(int item) {
      if (isCanLoopSafely()) {
        mViewPager.setCurrentItem(BannerUtils.getOriginalPosition(mBannerPagerAdapter.getListSize()) + item,
            false);
      } else {
        mViewPager.setCurrentItem(item, false);
      }
    }

    private void refreshIndicator(List<? extends T> data) {
      setIndicatorValues(data);
      mBannerManager.getBannerOptions().getIndicatorOptions()
          .setCurrentPosition(BannerUtils.getRealPosition(mViewPager.getCurrentItem(), data.size()));
      mIndicatorView.notifyDataChanged();
    }

    private static final String KEY_SUPER_STATE = "SUPER_STATE";
    private static final String KEY_CURRENT_POSITION = "CURRENT_POSITION";
    private static final String KEY_IS_CUSTOM_INDICATOR = "IS_CUSTOM_INDICATOR";

    private int getInterval() {
      return mBannerManager.getBannerOptions().getInterval();
    }

    private boolean isAutoPlay() {
      return mBannerManager.getBannerOptions().isAutoPlay();
    }

    private boolean isCanLoopSafely() {
      return mBannerManager != null && mBannerManager.getBannerOptions() != null
          && mBannerManager.getBannerOptions().isCanLoop()
          && mBannerPagerAdapter != null
          && mBannerPagerAdapter.getListSize() > 1;
    }

    @Nullable
    @Override
    protected Parcelable onSaveInstanceState() {
      Parcelable superState = super.onSaveInstanceState();
      Bundle bundle = new Bundle();
      bundle.putParcelable(KEY_SUPER_STATE, superState);
      bundle.putInt(KEY_CURRENT_POSITION, currentPosition);
      bundle.putBoolean(KEY_IS_CUSTOM_INDICATOR, isCustomIndicator);
      return bundle;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
      Bundle bundle = (Bundle) state;
      Parcelable superState = bundle.getParcelable(KEY_SUPER_STATE);
      super.onRestoreInstanceState(superState);
      currentPosition = bundle.getInt(KEY_CURRENT_POSITION);
      isCustomIndicator = bundle.getBoolean(KEY_IS_CUSTOM_INDICATOR);
      setCurrentItem(currentPosition, false);
    }

    /**
     * @return BannerViewPager data set
     */
    public List<T> getData() {
      if (mBannerPagerAdapter != null) {
        return mBannerPagerAdapter.getData();
      }
      return Collections.emptyList();
    }

    /**
     * Start loop
     */
    public void startLoop() {
      if (!isLooping && isAutoPlay() && mBannerPagerAdapter != null && mBannerPagerAdapter.getListSize() > 1 && isAttachedToWindow() && getInterval() > 0) {
        mHandler.postDelayed(mRunnable, getInterval());
        isLooping = true;
      }
    }

    /**
     * Start loop immediately
     */
    public void startLoopNow() {
      if (!isLooping && isAutoPlay() && mBannerPagerAdapter != null &&
          mBannerPagerAdapter.getListSize() > 1) {
        mHandler.post(mRunnable);
        isLooping = true;
      }
    }

    /**
     * Stop loop
     */
    public void stopLoop() {
      if (isLooping) {
        mHandler.removeCallbacks(mRunnable);
        isLooping = false;
      }
    }

    public BannerViewPager<T> setAdapter(BaseBannerAdapter<T> adapter) {
      this.mBannerPagerAdapter = adapter;
      return this;
    }

    public BaseBannerAdapter<T> getAdapter() {
      return mBannerPagerAdapter;
    }

    /**
     * Set round rectangle effect for BannerViewPager.
     *
     * @param radius round radius
     */
    public BannerViewPager<T> setRoundCorner(int radius) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        mBannerManager.getBannerOptions().setRoundRectRadius(radius);
      } else {
        setRoundCorner(radius, radius, radius, radius);
      }
      return this;
    }

    /**
     * Set round rectangle effect for BannerViewPager.
     *
     * @param topLeftRadius top left round radius
     * @param topRightRadius top right round radius
     * @param bottomLeftRadius bottom left round radius
     * @param bottomRightRadius bottom right round radius
     */
    public BannerViewPager<T> setRoundCorner(int topLeftRadius, int topRightRadius,
        int bottomLeftRadius,
        int bottomRightRadius) {
      mRadiusRectF = new RectF();
      mRadiusPath = new Path();
      mBannerManager.getBannerOptions()
          .setRoundRectRadius(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
      return this;
    }

    /**
     * Enable/disable auto play
     *
     * @param autoPlay is enable auto play
     */
    public BannerViewPager<T> setAutoPlay(boolean autoPlay) {
      mBannerManager.getBannerOptions().setAutoPlay(autoPlay);
      if (isAutoPlay()) {
        mBannerManager.getBannerOptions().setCanLoop(true);
      }
      return this;
    }

    /**
     * Enable/disable loop
     *
     * @param canLoop is can loop
     */
    public BannerViewPager<T> setCanLoop(boolean canLoop) {
      mBannerManager.getBannerOptions().setCanLoop(canLoop);
      if (!canLoop) {
        mBannerManager.getBannerOptions().setAutoPlay(false);
      }
      return this;
    }

    /**
     * Set loop interval
     *
     * @param interval loop interval,unit is millisecond.
     */
    public BannerViewPager<T> setInterval(int interval) {
      mBannerManager.getBannerOptions().setInterval(interval);
      return this;
    }

    /**
     * @param transformer PageTransformer that will modify each page's animation properties
     */
    public BannerViewPager<T> setPageTransformer(@Nullable ViewPager2.PageTransformer transformer) {
      if (transformer != null) {
        mViewPager.setPageTransformer(transformer);
      }
      return this;
    }

    /**
     * @param transformer PageTransformer that will modify each page's animation properties
     */
    public BannerViewPager<T> addPageTransformer(@Nullable ViewPager2.PageTransformer transformer) {
      if (transformer != null) {
        mBannerManager.addTransformer(transformer);
      }
      return this;
    }

    public void removeTransformer(@Nullable ViewPager2.PageTransformer transformer) {
      if (transformer != null) {
        mBannerManager.removeTransformer(transformer);
      }
    }

    public void removeDefaultPageTransformer() {
      mBannerManager.removeDefaultPageTransformer();
    }

    public void removeMarginPageTransformer() {
      mBannerManager.removeMarginPageTransformer();
    }

    /**
     * set page margin
     *
     * @param pageMargin page margin
     */
    public BannerViewPager<T> setPageMargin(int pageMargin) {
      mBannerManager.setPageMargin(pageMargin);
      return this;
    }

    /**
     * set item click listener
     *
     * @param onPageClickListener item click listener
     */
    public BannerViewPager<T> setOnPageClickListener(OnPageClickListener onPageClickListener) {
      this.mOnPageClickListener = onPageClickListener;
      if (mBannerPagerAdapter != null) {
        mBannerPagerAdapter.setPageClickListener(mOnPageClickListener);
      }
      return this;
    }

    /**
     * Set page scroll duration
     *
     * @param scrollDuration page scroll duration
     */
    public BannerViewPager<T> setScrollDuration(int scrollDuration) {
      mBannerManager.getBannerOptions().setScrollDuration(scrollDuration);
      return this;
    }

    /**
     * set indicator color
     *
     * @param checkedColor checked color of indicator
     * @param normalColor unchecked color of indicator
     */
    public BannerViewPager<T> setIndicatorSliderColor(@ColorInt int normalColor,
        @ColorInt int checkedColor) {
      mBannerManager.getBannerOptions().setIndicatorSliderColor(normalColor, checkedColor);
      return this;
    }

    public BannerViewPager<T> setIndicatorSliderRadius(int radius) {
      setIndicatorSliderRadius(radius, radius);
      return this;
    }

    /**
     * set indicator circle radius
     *
     * @param normalRadius unchecked circle radius
     * @param checkedRadius checked circle radius
     */
    public BannerViewPager<T> setIndicatorSliderRadius(int normalRadius, int checkedRadius) {
      mBannerManager.getBannerOptions().setIndicatorSliderWidth(normalRadius * 2, checkedRadius * 2);
      return this;
    }

    public BannerViewPager<T> setIndicatorSliderWidth(int indicatorWidth) {
      setIndicatorSliderWidth(indicatorWidth, indicatorWidth);
      return this;
    }

    public BannerViewPager<T> setIndicatorSliderWidth(int normalWidth, int checkWidth) {
      mBannerManager.getBannerOptions().setIndicatorSliderWidth(normalWidth, checkWidth);
      return this;
    }

    public BannerViewPager<T> setIndicatorHeight(int indicatorHeight) {
      mBannerManager.getBannerOptions().setIndicatorHeight(indicatorHeight);
      return this;
    }

    /**
     * Set Indicator gap of dash/circle
     *
     * @param indicatorGap indicator gap
     */
    public BannerViewPager<T> setIndicatorSliderGap(int indicatorGap) {
      mBannerManager.getBannerOptions().setIndicatorGap(indicatorGap);
      return this;
    }

    /**
     * Set the visibility state of indicator view.
     *
     * @param visibility One of {@link View#VISIBLE}, {@link View#INVISIBLE}, or {@link View#GONE}.
     */
    public BannerViewPager<T> setIndicatorVisibility(@Visibility int visibility) {
      mBannerManager.getBannerOptions().setIndicatorVisibility(visibility);
      return this;
    }

    /**
     * set indicator gravity in BannerViewPager
     *
     * @param gravity indicator gravity

     */
    public BannerViewPager<T> setIndicatorGravity(@AIndicatorGravity int gravity) {
      mBannerManager.getBannerOptions().setIndicatorGravity(gravity);
      return this;
    }

    public BannerViewPager<T> setIndicatorSlideMode(@AIndicatorSlideMode int slideMode) {
      mBannerManager.getBannerOptions().setIndicatorSlideMode(slideMode);
      return this;
    }

    /**
     * Set custom indicator.
     * the custom indicator view must extends BaseIndicator or implements IIndicator
     *
     * @param customIndicator custom indicator view
     */
    public BannerViewPager<T> setIndicatorView(IIndicator customIndicator) {
      if (customIndicator instanceof View) {
        isCustomIndicator = true;
        mIndicatorView = customIndicator;
      }
      return this;
    }


    public BannerViewPager<T> setIndicatorStyle(@AIndicatorStyle int indicatorStyle) {
      mBannerManager.getBannerOptions().setIndicatorStyle(indicatorStyle);
      return this;
    }

    /**
     * Create BannerViewPager with data.
     * If data has fetched when create BannerViewPager,you can call this method.
     */
    public void create(List<T> data, boolean registerOnPageChangeCallback) {
      if (mBannerPagerAdapter == null) {
        throw new NullPointerException("You must set adapter for BannerViewPager");
      }
      mBannerPagerAdapter.setData(data);
      initBannerData(registerOnPageChangeCallback);
    }

    /**
     * Create BannerViewPager with no data
     * If there is no data while you create BannerViewPager(for example,The data is from remote
     * server)，you can call this method.
     * Then,while you fetch data successfully,just need call {@link #refreshData(List)} method to
     * refresh.
     */
    public void create() {
      create(true);
    }

    public void create(boolean registerOnPageChangeCallback) {
      create(new ArrayList<>(), registerOnPageChangeCallback);
    }

    /**
     * Sets the orientation of the ViewPager2.
     *
     * @param orientation {@link ViewPager2#ORIENTATION_HORIZONTAL} or
     * {@link ViewPager2#ORIENTATION_VERTICAL}
     */
    public BannerViewPager<T> setOrientation(@ViewPager2.Orientation int orientation) {
      mBannerManager.getBannerOptions().setOrientation(orientation);
      return this;
    }

    public void addItemDecoration(@NonNull RecyclerView.ItemDecoration decor, int index) {
      if (isCanLoopSafely()) {
        int pageSize = mBannerPagerAdapter.getListSize();
        int currentItem = mViewPager.getCurrentItem();
        boolean canLoop = mBannerManager.getBannerOptions().isCanLoop();
        int realPosition = BannerUtils.getRealPosition(currentItem, pageSize);
        if (currentItem != index) {
          if (index == 0 && realPosition == pageSize - 1) {
            mViewPager.addItemDecoration(decor, currentItem + 1);
          } else if (realPosition == 0 && index == pageSize - 1) {
            mViewPager.addItemDecoration(decor, currentItem - 1);
          } else {
            mViewPager.addItemDecoration(decor, currentItem + (index - realPosition));
          }
        }
      } else {
        mViewPager.addItemDecoration(decor, index);
      }
    }

    public void addItemDecoration(@NonNull RecyclerView.ItemDecoration decor) {
      mViewPager.addItemDecoration(decor);
    }

    /**
     * Refresh data.
     * Confirm the {@link #create()} or {@link #create(List)} method has been called,
     * else the data won't be shown.
     *
     * Fix #209 如果BVP没有 attach 到 Window 上的时候刷新 ViewPager2 就会导致
     * ViewPager2 的 currentItem 被 reset 为 0，故出现 BVP 的 item 快速滚动问题
     * 为了避免这一问题，只能在已经attach 到 Window 上的时候去刷新数据。
     */
    public void refreshData(List<? extends T> list) {
      post(() -> {
        if (isAttachedToWindow() && list != null && mBannerPagerAdapter != null) {
          stopLoop();
          mBannerPagerAdapter.setData(list);
          mBannerPagerAdapter.notifyDataSetChanged();
          resetCurrentItem(getCurrentItem());
          refreshIndicator(list);
          startLoop();
        }
      });
    }

    public void addData(List<? extends T> list) {
      if (isAttachedToWindow() && list != null && mBannerPagerAdapter != null) {
        List<T> data = mBannerPagerAdapter.getData();
        data.addAll(list);
        mBannerPagerAdapter.notifyDataSetChanged();
        resetCurrentItem(getCurrentItem());
        refreshIndicator(data);
      }
    }

    /**
     * Removes the item at the specified position in this list.
     *
     * @param index the index of the item to be removed
     */
    public void removeItem(int index) {
      List<T> data = mBannerPagerAdapter.getData();
      if (isAttachedToWindow() && index >= 0 && index < data.size()) {
        data.remove(index);
        mBannerPagerAdapter.notifyDataSetChanged();
        resetCurrentItem(getCurrentItem());
        refreshIndicator(data);
      }
    }

    /**
     * Inserts the specified element at the specified position in this list
     *
     * @param index index at which the specified element is to be inserted
     * @param item item element to be inserted
     */
    public void insertItem(int index, T item) {
      List<T> data = mBannerPagerAdapter.getData();
      if (isAttachedToWindow() && index >= 0 && index <= data.size()) {
        data.add(index, item);
        mBannerPagerAdapter.notifyDataSetChanged();
        resetCurrentItem(getCurrentItem());
        refreshIndicator(data);
      }
    }

    /**
     * @return the currently selected page position.
     */
    public int getCurrentItem() {
      return currentPosition;
    }

    /**
     * Set the currently selected page. If the ViewPager has already been through its first
     * layout with its current adapter there will be a smooth animated transition between
     * the current item and the specified item.
     *
     * @param item Item index to select
     */
    public void setCurrentItem(int item) {
      setCurrentItem(item, true);
    }

    /**
     * Set the currently selected page.
     *
     * @param item Item index to select
     * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately
     */
    public void setCurrentItem(int item, boolean smoothScroll) {
      if (isCanLoopSafely()) {
        int pageSize = mBannerPagerAdapter.getListSize();
        item = item >= pageSize ? pageSize - 1 : item;
        int currentItem = mViewPager.getCurrentItem();
        boolean canLoop = mBannerManager.getBannerOptions().isCanLoop();
        int realPosition = BannerUtils.getRealPosition(currentItem, pageSize);
        if (currentItem != item) {
          if (item == 0 && realPosition == pageSize - 1) {
            mViewPager.setCurrentItem(currentItem + 1, smoothScroll);
          } else if (realPosition == 0 && item == pageSize - 1) {
            mViewPager.setCurrentItem(currentItem - 1, smoothScroll);
          } else {
            mViewPager.setCurrentItem(currentItem + (item - realPosition), smoothScroll);
          }
        }
      } else {
        mViewPager.setCurrentItem(item, smoothScroll);
      }
    }

    /**
     * Set the default PageTrans former for {@link ViewPager2}
     * Option params:
     * {@link PageStyle#MULTI_PAGE_OVERLAP}
     * {@link PageStyle#MULTI_PAGE_SCALE}
     * {@link PageStyle#NORMAL}
     */
    public BannerViewPager<T> setPageStyle(@APageStyle int pageStyle) {
      return setPageStyle(pageStyle, ScaleInTransformer.DEFAULT_MIN_SCALE);
    }

    public BannerViewPager<T> setPageStyle(@APageStyle int pageStyle, float pageScale) {
      mBannerManager.getBannerOptions().setPageStyle(pageStyle);
      mBannerManager.getBannerOptions().setPageScale(pageScale);
      return this;
    }

    /**
     * @param revealWidth In the multi-page mode, The exposed width of the items on the left and right
     * sides
     */
    public BannerViewPager<T> setRevealWidth(int revealWidth) {
      setRevealWidth(revealWidth, revealWidth);
      return this;
    }

    /**
     * This method is apply to multi-page mode {@link #setPageStyle(int)}
     *
     * @param leftRevealWidth The exposed width of left side
     * @param rightRevealWidth The exposed width of right side
     */
    public BannerViewPager<T> setRevealWidth(int leftRevealWidth, int rightRevealWidth) {
      mBannerManager.getBannerOptions().setRightRevealWidth(rightRevealWidth);
      mBannerManager.getBannerOptions().setLeftRevealWidth(leftRevealWidth);
      return this;
    }

    /**
     * Suggest to use default offScreenPageLimit.
     */
    public BannerViewPager<T> setOffScreenPageLimit(int offScreenPageLimit) {
      mBannerManager.getBannerOptions().setOffScreenPageLimit(offScreenPageLimit);
      return this;
    }

    public BannerViewPager<T> setIndicatorMargin(int left, int top, int right, int bottom) {
      mBannerManager.getBannerOptions().setIndicatorMargin(left, top, right, bottom);
      return this;
    }

    /**
     * Enable or disable user initiated scrolling
     */
    public BannerViewPager<T> setUserInputEnabled(boolean userInputEnabled) {
      mBannerManager.getBannerOptions().setUserInputEnabled(userInputEnabled);
      mViewPager.setUserInputEnabled(userInputEnabled);
      return this;
    }

    public interface OnPageClickListener {
      void onPageClick(View clickedView, int position);
    }

    public BannerViewPager<T> registerOnPageChangeCallback(
        ViewPager2.OnPageChangeCallback onPageChangeCallback) {
      this.onPageChangeCallback = onPageChangeCallback;
      return this;
    }

    public BannerViewPager<T> setLifecycleRegistry(Lifecycle lifecycleRegistry) {
      lifecycleRegistry.addObserver(this);
      return this;
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void onPause() {
      stopLoop();
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume() {
      if (!isStopLoopWhenDetachedFromWindow()) {
        startLoop();
      }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
      stopLoop();
    }

    /**
     * 设置是否允许在BVP的{@link MotionEvent#ACTION_DOWN}事件中禁止父View对事件的拦截，该方法
     * 用于解决CoordinatorLayout+CollapsingToolbarLayout在嵌套BVP时引起的滑动冲突问题。
     * <p>
     * BVP在处理ViewPager2嵌套滑动冲突时，在{@link #onInterceptTouchEvent(MotionEvent)}
     * 方法的{@link MotionEvent#ACTION_DOWN}事件中禁止了BVP的父View对触摸事件的拦截，
     * 导致CollapsingToolbarLayout的布局无法获取{@link MotionEvent#ACTION_DOWN}事件，
     * 致使CollapsingToolbarLayout无法处理down事件后的一系列事件而无法滑动。
     * 对于这种情况可以调用该方法不允许在BVP在{@link MotionEvent#ACTION_DOWN}事件中禁止父View的事件拦截。
     * </p>
     * 调用该方法将disallowIntercept设置为true后虽然解决了滑动冲突，但也会造成一定的不良影响，即如果BVP设置
     * 水平滑动，同时BVP外部也是可以水平滑动的ViewPager，则存在较小概率的滑动冲突，即滑动BVP的同时可能会触发
     * 外部ViewPager的滑动。但这一问题到目前为止似乎没有好的解决方案。
     *
     * @param disallowParentInterceptDownEvent 是否允许BVP在{@link MotionEvent#ACTION_DOWN}事件中禁止父View拦截事件，默认值为false
     * true 不允许BVP在{@link MotionEvent#ACTION_DOWN}时间中禁止父View的时间拦截，
     * 设置disallowIntercept为true可以解决CoordinatorLayout+CollapsingToolbarLayout的滑动冲突
     * false 允许BVP在{@link MotionEvent#ACTION_DOWN}时间中禁止父View的时间拦截，
     */

    public BannerViewPager<T> disallowParentInterceptDownEvent(
        boolean disallowParentInterceptDownEvent) {
      mBannerManager.getBannerOptions()
          .setDisallowParentInterceptDownEvent(disallowParentInterceptDownEvent);
      return this;
    }

    /**
     * Set right to left mode.
     *
     * @param rtlMode true:right to left mode,
     * false:right to left mode.
     */
    public BannerViewPager<T> setRTLMode(boolean rtlMode) {
      mViewPager.setLayoutDirection(rtlMode ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
      mBannerManager.getBannerOptions().setRtl(rtlMode);
      return this;
    }

    /**
     * @param stopLoopWhenDetachedFromWindow 当BVP滑动出屏幕的时候是否要停止轮播，
     *
     * true:滑动出屏幕停止自动轮播，false:滑动出屏幕继续自动轮播。默认值为true
     */
    public BannerViewPager<T> stopLoopWhenDetachedFromWindow(boolean stopLoopWhenDetachedFromWindow) {
      mBannerManager.getBannerOptions()
          .setStopLoopWhenDetachedFromWindow(stopLoopWhenDetachedFromWindow);
      return this;
    }

    /**
     * @param showIndicatorWhenOneItem 只有一个item时是否显示指示器，
     * true：显示，false：不显示，默认值false
     */
    public BannerViewPager<T> showIndicatorWhenOneItem(boolean showIndicatorWhenOneItem) {
      mBannerManager.getBannerOptions()
          .showIndicatorWhenOneItem(showIndicatorWhenOneItem);
      return this;
    }

    /**
     * @param autoScrollSmoothly is auto play scroll smoothly.
     */
    public BannerViewPager<T> setAutoPlaySmoothly(boolean autoScrollSmoothly) {
      mBannerManager.getBannerOptions()
          .setAutoScrollSmoothly(autoScrollSmoothly);
      return this;
    }

    /**
     * @deprecated Use {@link BannerViewPager#disallowParentInterceptDownEvent(boolean)} instead.
     */
    @Deprecated
    public BannerViewPager<T> disallowInterceptTouchEvent(boolean disallowIntercept) {
      mBannerManager.getBannerOptions().setDisallowParentInterceptDownEvent(disallowIntercept);
      return this;
    }

    /**
     * Set round rectangle effect for BannerViewPager.
     *
     * @param radius round radius
     * @deprecated Use {@link #setRoundCorner(int)} instead.
     */
    @Deprecated
    public BannerViewPager<T> setRoundRect(int radius) {
      return setRoundCorner(radius);
    }

    /**
     * Set round rectangle effect for BannerViewPager.
     *
     * @param topLeftRadius top left round radius
     * @param topRightRadius top right round radius
     * @param bottomLeftRadius bottom left round radius
     * @param bottomRightRadius bottom right round radius
     * @deprecated Use {@link #setRoundCorner(int, int, int, int)} instead.
     */
    @Deprecated
    public BannerViewPager<T> setRoundRect(int topLeftRadius, int topRightRadius,
        int bottomLeftRadius,
        int bottomRightRadius) {
      return setRoundCorner(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
    }
  }
